import * as vscode from 'vscode';
import { FunctionInfo, Definitions, CxxEleParameters, CxxElements, TextSnippetDefinitionsRequest } from '../entities/types';
import { DefinitionType } from '../entities/enum';
import { waitForTimeout } from '../utils/utils';
import { makeUnique } from '../utils/utils';
import { CxxLanguageServer } from '../services/CxxLanguageServer';
import { TreeSitter } from '../../../../core/treesitter/TreeSitter';
import { Logger } from '../../../../src/logger/Logger';

// 生成空定义
export function generateEmptyDefinitions() {
    return {
        struct: [], macro: [], typedef: [], function: [], class: [],
        union: [], enum: [], enumerator: [], variable: []
    };
}

export class CxxElementExtractor {
    private languageServer = CxxLanguageServer.getInstance();
    private treeSitterParser = TreeSitter.getInstance();
    private readonly fastElementRemainMs = 100;

    // C语言关键字
    public cKeywords = ['auto', 'break', 'case', 'char', 'const', 'continue', 'default', 'do', 'double', 'else',
        'enum', 'extern', 'float', 'for', 'goto', 'if', 'int', 'long', 'register', 'return', 'short', 'signed',
        'sizeof', 'static', 'struct', 'switch', 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while',
    ];

    /**
     * 获取Cxx的元素
     * @param param 上层传参
     * @param cursorFuncInfo 函数信息
     */
    public async getElements(param: CxxEleParameters, cursorFuncInfo: FunctionInfo): Promise<CxxElements> {
        // 不耗时的作为必选项
        const funcName = cursorFuncInfo?.sourceFuncName;
        const funcFullBody = cursorFuncInfo?.code;
        const definitionInFunction: Definitions = await this.getCursorFuncSymbolDef(param, { funcName, funcFullBody });
        return {
            symbolDefinitions: definitionInFunction,
        };
    }

    /**
     * 获取光标函数内的符号定义，支持函数开头到光标/整个函数的符号定义获取
     * （高耗时）
     * 
     * @param eleParam    元素传参
     * @param funcEles 函数信息
     * @return 函数定义对象
     */
    public async getCursorFuncSymbolDef(
        eleParam: CxxEleParameters,
        funcEles: {
            funcName: string | undefined,
            funcFullBody: string | undefined
        },
    ): Promise<Definitions> {
        try {
            const funcRangeStr = funcEles.funcFullBody;
            if (!funcEles.funcName || !funcRangeStr) {
                return generateEmptyDefinitions();
            }
            const definitions: Promise<Definitions> = this.getTextSnippetSymbolDef(
                eleParam.doc,
                funcRangeStr,
                funcEles.funcName,
                eleParam.cancellationToken,
                eleParam.funcDefNeedFilterSymbols ?? []
            );
            const globalTimeout = eleParam?.timeoutMs;
            if (!globalTimeout) {
                // 没有任何超时机制
                return await definitions;
            }
            let timeout = Math.max(0, Math.min(Infinity, globalTimeout ?? Infinity));
            if (timeout === globalTimeout && globalTimeout) {
                timeout = globalTimeout - this.fastElementRemainMs; // 超时时间和全局超时相等时，需要给其他非耗时元素预留获取一点时间
            }
            try {
                return await Promise.race([
                    definitions,
                    waitForTimeout(timeout, eleParam.cancellationToken)
                ]) ?? generateEmptyDefinitions();
            } catch (err) {
                if (err instanceof Error) {
                    Logger.warn(`getCursorFuncSymbolDef error in ${funcEles.funcName}: ${err.message}`);
                }
                return generateEmptyDefinitions();
            }
        } catch (err) {
            if (err instanceof Error) {
                Logger.warn(`getCursorFuncSymbolDef error in ${funcEles.funcName}: ${err.message}`);
            }
            return generateEmptyDefinitions();
        }
    }

    /**
     * 获取某个代码片段的相关的宏、结构体、类型定义
     * （高耗时）
     * 
     * @param doc 文档
     * @param code 代码片段
     * @param funcName 函数名
     * @param cancellationToken 取消令牌
     * @param needFilteredSymbols 需要过滤的符号（可选的）
     * @returns 函数相关的宏、结构体、类型定义
     */
    public async getTextSnippetSymbolDef(
        doc: vscode.TextDocument,
        code: string,
        funcName: string,
        cancellationToken?: vscode.CancellationToken,
        needFilteredSymbols?: string[]
    ): Promise<Definitions> {
        const filteredSymbols = [...needFilteredSymbols || [], ...this.cKeywords];
        const functionSymbols: string[] = await this.getSymbolsInSnippet(doc.languageId, code, filteredSymbols);
        const textSnippet: TextSnippetDefinitionsRequest = {
            symbols: functionSymbols,
            token: cancellationToken,
        };
        const start = Date.now(); // 记录开始时间
        const definitions = await this.languageServer.getTextSnippetDefinition(textSnippet, cancellationToken);
        const res: Definitions = generateEmptyDefinitions();
        // 出现同名但是位置不同的多个definition时，只取第一个
        definitions.forEach((def) => {
            // 过滤函数自身
            if (def.name === funcName && def.location.uri.path === doc.uri.path) {
                return;
            }
            const regexStruct = /^typedef(\s+)struct/g;
            const regexEnum = /^typedef(\s+)enum/g;
            if (def.type === DefinitionType.typedef && regexStruct.test(def.text)) {
                res.struct.push(def);
            } else if (def.type === DefinitionType.typedef && regexEnum.test(def.text)) {
                res.enum.push(def);
            } else {
                res[def.type as keyof Definitions]!.push(def);
            }
        });
        for (const key in res) {
            res[key as keyof Definitions] = makeUnique(
                res[key as keyof Definitions],
                (element: any) => element.name
            );
        }
        const end = Date.now(); // 记录结束时间
        const executionTime = end - start; // 计算执行时间
        Logger.info(`languageServer.getTextSnippetDefinition time cost: ${executionTime}ms.`);
        return res;
    }

    protected async getSymbolsInSnippet(languageId: string, codeSnippet: string,
        filteredSymbols: string[]): Promise<string[]> {
        const symbols = await this.treeSitterParser.getSymbols(languageId, codeSnippet);
        // 过滤关键字,常见符号以及纯数字
        const validSymbols = symbols.filter(s => !filteredSymbols.some(item => item.toLowerCase() === s.toLowerCase())
            && isNaN(Number(s)));
        return validSymbols;
    }
}

